package main

import (
	"io"
	"log"
	"net/http"
	_ "net/http/pprof"
	"os"
	"strings"
)

/*
使用pprof可以实现监控
*/
func main() {

	/*
		二. 使用http. HandleFunc()方法
			1. http. HandleFunc()的作用是注册网络访问的路由。因为它采用的是默认的路由分发任务方式，所以称之为默认的多路由分发服务；
			2. HandleFunc()的第一个参数是请求路径的匹配模式，第二个参数是一个函数类型，表示这个请求需要处理的事情；
			3. Go语言提供了一个ServeMux()方法去分发任务。HandleFunc()其实就是直接将参数交给DefaultServeMux()来进行处理；
			4. 通过http. HandleFunc()注册网络路由时，http.ListenAndServer()的第二个参数通常为nil，
			   这意味着服务端采用默认的http.DefaultServeMux进行分发处理
	*/

	// 绑定路径，去触发方法
	http.HandleFunc("/index", indexHandler)
	http.HandleFunc("/login", loginActionHandler)
	http.HandleFunc("/postLogin", postFormHandler)
	http.HandleFunc("/postMulti", postMultipart)

	// 第一个参数为监听地址，第二个参数便是服务端处理程序，通常为nil，这意味着服务端采用默认的http.DefaultServeMux进行分发处理
	_ = http.ListenAndServe(":2003", nil)

	/*
		三. 使用http.NewServeMux()方法。TIPS：如需使用自行查阅
			http. NewServeMux ()的作用是注册网络访问的多路路由。因为它采用的是自定义的多路由分发任务方式，所以称之为自定义多路由分发服务
	*/
}

/*
首页handler
*/
func indexHandler(w http.ResponseWriter, r *http.Request) {

	log.Println("index >>>>>>")
	_, _ = w.Write([]byte("这是默认首页"))
}

/*
登录handler。介绍获取GET方式传递的参数
常见的获取方式（r表示*http.Request类型，w表示http.ResponseWriter类型）：
	1. r.ParseForm()，返回error，判断是否解析传参时出错；
	2. r.Form属性，返回url.Values，它是map[string][]string类型。一般情况下，使用r.FormValue(key)方法更简洁方便；
	3. r. FormValue(key)方法可以根据客户端传参的key获取对应的值
*/
func loginActionHandler(w http.ResponseWriter, r *http.Request) {

	if r.Method == "GET" && r.ParseForm() == nil {
		userName := r.FormValue("userName")
		password := r.FormValue("password")
		if len(userName) < 8 || len(userName) > 20 {
			_, _ = w.Write([]byte("用户名长度错误"))
			return
		}
		if len(password) < 10 || len(password) > 30 {
			_, _ = w.Write([]byte("密码不符合规范"))
			return
		}
		// 页面跳转
		http.Redirect(w, r, "/index", http.StatusOK)
		return
	} else {
		_, _ = w.Write([]byte("请求不合法"))
		return
	}
}

/*
获取POST方式传递的参数。
普通的post表单请求：Content-Type=application/x-www-form-urlencoded
*/
func postFormHandler(w http.ResponseWriter, r *http.Request) {

	if r.Method == "POST" && r.PostForm == nil {
		name := r.PostFormValue("userName")
		password := r.PostFormValue("password")
		if len(name) < 8 || len(name) > 20 {
			_, _ = w.Write([]byte("用户名长度错误"))
			return
		}
		if len(password) < 10 || len(password) > 30 {
			_, _ = w.Write([]byte("密码不符合规范"))
			return
		}
		// 页面跳转
		http.Redirect(w, r, "/index", http.StatusOK)
	} else {
		_, _ = w.Write([]byte("请求不合法"))
		return
	}
}

/*
获取POST方式传递的参数。
有文件上传的表单：Content-Type=multipart/form-data
TIPS：
	需要使用r.FormFile(key)方法，根据文件上传控件的name取到上传文件的File和FileHeader对象。这样就能获取上传文件的名称、后缀、尺寸、具体的字节，
	然后通过io的copy()操作实现文件上传
*/
func postMultipart(w http.ResponseWriter, r *http.Request) {

	if r.Method == "POST" {

		// 使用r.FormFile(key)方法 得到文件和 FileHeader
		file, header, err := r.FormFile("file")
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		// 判断文件类型是否正确
		filename := header.Filename
		split := strings.Split(filename, ".")
		for i, s := range split {
			b := s != "jpg" && s != "jpeg" && s != "png"
			if i == len(split) && b {
				_, _ = w.Write([]byte("格式不对"))
				return
			}
		}
		defer file.Close()
		// 创建文件
		create, err := os.Create("./" + filename)
		if err != nil {
			_, _ = w.Write([]byte("创建文件失败"))
			return
		}
		defer create.Close()
		// 复制文件
		_, err = io.Copy(create, file)
		if err != nil {
			_, _ = w.Write([]byte("复制文件失败"))
			return
		}
		_, _ = w.Write([]byte("上传文件成功"))
		return
	}
	_, _ = w.Write([]byte("请求方式错误"))
}
